//------------------------------------------------------------------------------
// PropVariant.h
//------------------------------------------------------------------------------
// File provided for Microsoft Shared Source.
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 
// PARTICULAR PURPOSE.
//------------------------------------------------------------------------------
//
// PROPVARIANT wrapper class
//
//------------------------------------------------------------------------------

#pragma once

#pragma comment (lib, "propsys.lib")
#include "propkey.h"

#include <propvarutil.h>
#include "MemUtils.h"

class CComPropVariant : public tagPROPVARIANT
{
// Constructors
public:
	CComPropVariant() throw()
	{
		::PropVariantInit(this);
	}
	~CComPropVariant() throw()
	{
		Clear();
	}

	CComPropVariant(__in const PROPVARIANT& varSrc)
	{
		vt = VT_EMPTY;
		InternalCopy(&varSrc);
	}

	CComPropVariant(__in const CComPropVariant& varSrc)
	{
		vt = VT_EMPTY;
		InternalCopy(&varSrc);
	}
	CComPropVariant(__in LPCOLESTR lpszSrc, __in VARTYPE vtSrc = VT_LPWSTR)
	{
        ATLASSERT(vtSrc == VT_LPWSTR || vtSrc == VT_BSTR);
		vt = VT_EMPTY;
		*this = lpszSrc;
	}

	CComPropVariant(__in LPCSTR lpszSrc)
	{
		vt = VT_EMPTY;
		*this = lpszSrc;
	}

	CComPropVariant(__in bool bSrc)
	{
		vt = VT_BOOL;
		boolVal = bSrc ? ATL_VARIANT_TRUE : ATL_VARIANT_FALSE;
	}

	CComPropVariant(__in int nSrc, __in VARTYPE vtSrc = VT_I4) throw()
	{
		ATLASSERT(vtSrc == VT_I4 || vtSrc == VT_INT);
		vt = vtSrc;
		intVal = nSrc;
	}
	CComPropVariant(__in BYTE nSrc) throw()
	{
		vt = VT_UI1;
		bVal = nSrc;
	}
	CComPropVariant(__in short nSrc) throw()
	{
		vt = VT_I2;
		iVal = nSrc;
	}
	CComPropVariant(__in long nSrc, __in VARTYPE vtSrc = VT_I4) throw()
	{
		ATLASSERT(vtSrc == VT_I4 || vtSrc == VT_ERROR);
		vt = vtSrc;
		lVal = nSrc;
	}
	CComPropVariant(__in float fltSrc) throw()
	{
		vt = VT_R4;
		fltVal = fltSrc;
	}
	CComPropVariant(__in double dblSrc, __in VARTYPE vtSrc = VT_R8) throw()
	{
		ATLASSERT(vtSrc == VT_R8 || vtSrc == VT_DATE);
		vt = vtSrc;
		dblVal = dblSrc;
	}
	CComPropVariant(CY __in cySrc) throw()
	{
		vt = VT_CY;
		cyVal.Hi = cySrc.Hi;
		cyVal.Lo = cySrc.Lo;
	}
	CComPropVariant(__in_opt IDispatch* pSrc) throw()
	{
		vt = VT_DISPATCH;
		pdispVal = pSrc;
		// Need to AddRef as VariantClear will Release
		if (pdispVal != NULL)
			pdispVal->AddRef();
	}
	CComPropVariant(__in_opt IUnknown* pSrc) throw()
	{
		vt = VT_UNKNOWN;
		punkVal = pSrc;
		// Need to AddRef as VariantClear will Release
		if (punkVal != NULL)
			punkVal->AddRef();
	}
	CComPropVariant(__in char cSrc) throw()
	{
		vt = VT_I1;
		cVal = cSrc;
	}
	CComPropVariant(__in unsigned short nSrc) throw()
	{
		vt = VT_UI2;
		uiVal = nSrc;
	}
	CComPropVariant(__in unsigned long nSrc) throw()
	{
		vt = VT_UI4;
		ulVal = nSrc;
	}
	CComPropVariant(__in unsigned int nSrc, __in VARTYPE vtSrc = VT_UI4) throw()
	{
		ATLASSERT(vtSrc == VT_UI4 || vtSrc == VT_UINT);
		vt = vtSrc;
		uintVal= nSrc;
	}
	CComPropVariant(__in const CComBSTR& bstrSrc)
	{
		vt = VT_EMPTY;
		*this = bstrSrc;
	}
	CComPropVariant(__in_opt const SAFEARRAY *pSrc)
	{
		LPSAFEARRAY pCopy;
		if (pSrc != NULL)
		{
			HRESULT hRes = ::SafeArrayCopy((LPSAFEARRAY)pSrc, &pCopy);
			if (SUCCEEDED(hRes) && pCopy != NULL)
			{
                ::ATL::AtlSafeArrayGetActualVartype((LPSAFEARRAY)pSrc, &vt);
				vt |= VT_ARRAY;
				parray = pCopy;
			}
			else
			{
				vt = VT_ERROR;
				scode = hRes;
#ifndef _ATL_NO_VARIANT_THROW
				ATLENSURE_THROW(FALSE, E_OUTOFMEMORY);
#endif
			}
		}
		else
		{
			vt = VT_EMPTY;
		}
	}
    CComPropVariant (__in ULONGLONG ull)
    {
        vt = VT_UI8;
        uhVal.QuadPart = ull;
    }

    operator ULONG()
    {
        ATLASSERT(vt == VT_UI4);
        return ulVal;
    }

    operator ULONGLONG()
    {
        ATLASSERT(vt == VT_UI8);
        return uhVal.QuadPart;
    }

		operator LPCWSTR ()
		{
				CStringW dst;

		}
		

// Assignment Operators
public:
	CComPropVariant& operator=(__in const CComPropVariant& varSrc)
	{
        if(this!=&varSrc)
        {
		    InternalCopy(&varSrc);
        }
		return *this;
	}
	CComPropVariant& operator=(__in const PROPVARIANT& varSrc)
	{
        if(static_cast<PROPVARIANT *>(this)!=&varSrc)
        {
		    InternalCopy(&varSrc);
        }
		return *this;
	}

	CComPropVariant& operator=(__in const CComBSTR& bstrSrc)
	{
		Clear();
		vt = VT_BSTR;
		bstrVal = bstrSrc.Copy();
#pragma warning(push)
#pragma warning(disable:4068)
#pragma prefast(push)
#pragma prefast(disable:325, "We are checking allocation semantics here")
		if (bstrVal == NULL && bstrSrc.m_str != NULL)
		{
			vt = VT_ERROR;
			scode = E_OUTOFMEMORY;
#ifndef _ATL_NO_VARIANT_THROW
			ATLENSURE_THROW(FALSE, E_OUTOFMEMORY);
#endif
		}
#pragma prefast(pop)
#pragma warning(pop)
		return *this;
	}

	CComPropVariant& operator=(__in LPCOLESTR lpszSrc)
	{
		Clear();
		vt = VT_LPWSTR;
        if (FAILED(SHStrDup(lpszSrc, &pwszVal)))
        {
			vt = VT_ERROR;
			scode = E_OUTOFMEMORY;
#ifndef _ATL_NO_VARIANT_THROW
			ATLENSURE_THROW(FALSE, E_OUTOFMEMORY);
#endif

		}
		return *this;
	}

	CComPropVariant& operator=(__in LPCSTR lpszSrc)
	{
		USES_CONVERSION_EX;
		Clear();
		vt = VT_BSTR;
		bstrVal = ::SysAllocString(A2COLE_EX(lpszSrc, _ATL_SAFE_ALLOCA_DEF_THRESHOLD));

		if (bstrVal == NULL && lpszSrc != NULL)
		{
			vt = VT_ERROR;
			scode = E_OUTOFMEMORY;
#ifndef _ATL_NO_VARIANT_THROW
			ATLENSURE_THROW(FALSE, E_OUTOFMEMORY);
#endif
		}
		return *this;
	}

	CComPropVariant& operator=(__in bool bSrc)
	{
		if (vt != VT_BOOL)
		{
			Clear();
			vt = VT_BOOL;
		}
		boolVal = bSrc ? ATL_VARIANT_TRUE : ATL_VARIANT_FALSE;
		return *this;
	}

	CComPropVariant& operator=(__in int nSrc) throw()
	{
		if (vt != VT_I4)
		{
			Clear();
			vt = VT_I4;
		}
		intVal = nSrc;

		return *this;
	}

	CComPropVariant& operator=(__in BYTE nSrc) throw()
	{
		if (vt != VT_UI1)
		{
			Clear();
			vt = VT_UI1;
		}
		bVal = nSrc;
		return *this;
	}

	CComPropVariant& operator=(__in short nSrc) throw()
	{
		if (vt != VT_I2)
		{
			Clear();
			vt = VT_I2;
		}
		iVal = nSrc;
		return *this;
	}

	CComPropVariant& operator=(__in long nSrc) throw()
	{
		if (vt != VT_I4)
		{
			Clear();
			vt = VT_I4;
		}
		lVal = nSrc;
		return *this;
	}

	CComPropVariant& operator=(__in float fltSrc) throw()
	{
		if (vt != VT_R4)
		{
			Clear();
			vt = VT_R4;
		}
		fltVal = fltSrc;
		return *this;
	}

	CComPropVariant& operator=(__in double dblSrc) throw()
	{
		if (vt != VT_R8)
		{
			Clear();
			vt = VT_R8;
		}
		dblVal = dblSrc;
		return *this;
	}

	CComPropVariant& operator=(ULONGLONG ullSrc) throw()
	{
			if (vt != VT_UI8)
			{
				Clear();
				vt = VT_UI8;
			}
			uhVal.QuadPart = ullSrc;
			return *this;
	}

	CComPropVariant& operator=(FILETIME flSrc) throw()
	{
			if (vt != VT_FILETIME)
			{
				Clear();
				vt = VT_FILETIME;
			}
			filetime = flSrc;
			return *this;
	}

	CComPropVariant& operator=(__in CY cySrc) throw()
	{
		if (vt != VT_CY)
		{
			Clear();
			vt = VT_CY;
		}
		cyVal.Hi = cySrc.Hi;
		cyVal.Lo = cySrc.Lo;
		return *this;
	}

	CComPropVariant& operator=(__in_opt IDispatch* pSrc) throw()
	{
		Clear();
		vt = VT_DISPATCH;
		pdispVal = pSrc;
		// Need to AddRef as VariantClear will Release
		if (pdispVal != NULL)
			pdispVal->AddRef();
		return *this;
	}

	CComPropVariant& operator=(__in_opt IUnknown* pSrc) throw()
	{
		Clear();
		vt = VT_UNKNOWN;
		punkVal = pSrc;

		// Need to AddRef as VariantClear will Release
		if (punkVal != NULL)
			punkVal->AddRef();
		return *this;
	}

	CComPropVariant& operator=(__in char cSrc) throw()
	{
		if (vt != VT_I1)
		{
			Clear();
			vt = VT_I1;
		}
		cVal = cSrc;
		return *this;
	}

	CComPropVariant& operator=(__in unsigned short nSrc) throw()
	{
		if (vt != VT_UI2)
		{
			Clear();
			vt = VT_UI2;
		}
		uiVal = nSrc;
		return *this;
	}

	CComPropVariant& operator=(__in unsigned long nSrc) throw()
	{
		if (vt != VT_UI4)
		{
			Clear();
			vt = VT_UI4;
		}
		ulVal = nSrc;
		return *this;
	}

	CComPropVariant& operator=(__in unsigned int nSrc) throw()
	{
		if (vt != VT_UI4)
		{
			Clear();
			vt = VT_UI4;
		}
		uintVal= nSrc;
		return *this;
	}

	CComPropVariant& operator=(__in_opt BYTE* pbSrc) throw()
	{
		if (vt != (VT_UI1|VT_BYREF))
		{
			Clear();
			vt = VT_UI1|VT_BYREF;
		}
		pbVal = pbSrc;
		return *this;
	}

	CComPropVariant& operator=(__in_opt short* pnSrc) throw()
	{
		if (vt != (VT_I2|VT_BYREF))
		{
			Clear();
			vt = VT_I2|VT_BYREF;
		}
		piVal = pnSrc;
		return *this;
	}

#ifdef _NATIVE_WCHAR_T_DEFINED
	CComPropVariant& operator=(__in_opt USHORT* pnSrc) throw()
	{
		if (vt != (VT_UI2|VT_BYREF))
		{
			Clear();
			vt = VT_UI2|VT_BYREF;
		}
		puiVal = pnSrc;
		return *this;
	}
#endif

	CComPropVariant& operator=(__in_opt int* pnSrc) throw()
	{
		if (vt != (VT_I4|VT_BYREF))
		{
			Clear();
			vt = VT_I4|VT_BYREF;
		}
		pintVal = pnSrc;
		return *this;
	}

	CComPropVariant& operator=(__in_opt UINT* pnSrc) throw()
	{
		if (vt != (VT_UI4|VT_BYREF))
		{
			Clear();
			vt = VT_UI4|VT_BYREF;
		}
		puintVal = pnSrc;
		return *this;
	}

	CComPropVariant& operator=(__in_opt long* pnSrc) throw()
	{
		if (vt != (VT_I4|VT_BYREF))
		{
			Clear();
			vt = VT_I4|VT_BYREF;
		}
		plVal = pnSrc;
		return *this;
	}

	CComPropVariant& operator=(__in_opt ULONG* pnSrc) throw()
	{
		if (vt != (VT_UI4|VT_BYREF))
		{
			Clear();
			vt = VT_UI4|VT_BYREF;
		}
		pulVal = pnSrc;
		return *this;
	}

	CComPropVariant& operator=(__in_opt float* pfSrc) throw()
	{
		if (vt != (VT_R4|VT_BYREF))
		{
			Clear();
			vt = VT_R4|VT_BYREF;
		}
		pfltVal = pfSrc;
		return *this;
	}

	CComPropVariant& operator=(__in_opt double* pfSrc) throw()
	{
		if (vt != (VT_R8|VT_BYREF))
		{
			Clear();
			vt = VT_R8|VT_BYREF;
		}
		pdblVal = pfSrc;
		return *this;
	}

	CComPropVariant& operator=(__in_opt const SAFEARRAY *pSrc)
	{
		Clear();
		LPSAFEARRAY pCopy;
		if (pSrc != NULL)
		{
			HRESULT hRes = ::SafeArrayCopy((LPSAFEARRAY)pSrc, &pCopy);
			if (SUCCEEDED(hRes) && pCopy != NULL)
			{
				::ATL::AtlSafeArrayGetActualVartype((LPSAFEARRAY)pSrc, &vt);
				vt |= VT_ARRAY;
				parray = pCopy;
			}
			else
			{
				vt = VT_ERROR;
				scode = hRes;
#ifndef _ATL_NO_VARIANT_THROW
			ATLENSURE_THROW(FALSE, E_OUTOFMEMORY);
#endif
			}
		}
		return *this;
	}

// Comparison Operators
public:
	int Compare (const PROPVARIANT& varSrc) const throw()
	{
		return PropVariantCompare(*this, varSrc);
	}

	bool operator==(__in const PROPVARIANT& varSrc) const throw()
	{
		// For backwards compatibility
		if (vt == VT_NULL && varSrc.vt == VT_NULL)
		{
			return true;
		}
		// Variants not equal if types don't match
		if (vt != varSrc.vt)
		{
			return false;
		}
		return Compare(varSrc) == static_cast<HRESULT>(VARCMP_EQ);
	}

	bool operator!=(__in const PROPVARIANT& varSrc) const throw()
	{
		return !operator==(varSrc);
	}

	bool operator<(__in const PROPVARIANT& varSrc) const throw()
	{
		if (vt == VT_NULL && varSrc.vt == VT_NULL)
			return false;
		return PropVariantCompare(*this, varSrc)== static_cast<HRESULT>(VARCMP_LT);
	}

	bool operator>(__in const PROPVARIANT& varSrc) const throw()
	{
		if (vt == VT_NULL && varSrc.vt == VT_NULL)
			return false;
		return PropVariantCompare(*this, varSrc)== static_cast<HRESULT>(VARCMP_GT);
	}

// Operations
public:
	HRESULT Clear() { return ::PropVariantClear(this); }
	HRESULT Copy(__in const PROPVARIANT* pSrc) { return ::PropVariantCopy(this, const_cast<PROPVARIANT*>(pSrc)); }
	// copy VARIANT to BSTR
	HRESULT CopyTo(__out BSTR *pstrDest)
	{
		ATLASSERT(pstrDest != NULL && vt == VT_BSTR);
		HRESULT hRes = E_POINTER;
		if (pstrDest != NULL && vt == VT_BSTR)
		{
			*pstrDest = ::SysAllocStringByteLen((char*)bstrVal, ::SysStringByteLen(bstrVal));
			if (*pstrDest == NULL)
				hRes = E_OUTOFMEMORY;
			else
				hRes = S_OK;
		}
		else if (vt != VT_BSTR)
			hRes = DISP_E_TYPEMISMATCH;
		return hRes;
	}
	HRESULT Attach(__in PROPVARIANT* pSrc)
	{
		if(pSrc == NULL)
			return E_INVALIDARG;
			
		// Clear out the variant
		HRESULT hr = Clear();
		if (!FAILED(hr))
		{
			// Copy the contents and give control to CComPropVariant
			Checked::memcpy_s(this, sizeof(CComPropVariant), pSrc, sizeof(PROPVARIANT));
			pSrc->vt = VT_EMPTY;
			hr = S_OK;
		}
		return hr;
	}

	HRESULT Detach(__out PROPVARIANT* pDest)
	{
		ATLASSERT(pDest != NULL);
		if(pDest == NULL)
			return E_POINTER;
			
		// Clear out the variant
		HRESULT hr = ::PropVariantClear(pDest);
		if (!FAILED(hr))
		{
			// Copy the contents and remove control from CComPropVariant
			Checked::memcpy_s(pDest, sizeof(PROPVARIANT), this, sizeof(PROPVARIANT));
			vt = VT_EMPTY;
			hr = S_OK;
		}
		return hr;
	}

	HRESULT ChangeType(__in VARTYPE vtNew, __in_opt const PROPVARIANT* pSrc = NULL)
	{
		PROPVARIANT* pVar = const_cast<PROPVARIANT*>(pSrc);
		// Convert in place if pSrc is NULL
		if (pVar == NULL)
			pVar = this;
		// Do nothing if doing in place convert and vts not different
		return ::PropVariantChangeType(this, *pVar, 0, vtNew);
	}

	template< typename T >
	void SetByRef( __in T* pT ) throw()
	{
		Clear();
		vt = CVarTypeInfo< T >::VT|VT_BYREF;
		byref = pT;
	}

	HRESULT WriteToStream(__inout IStream* pStream);
	HRESULT ReadFromStream(__inout IStream* pStream);

	// Return the size in bytes of the current contents
	ULONG GetSize() const;

// Implementation
public:
	HRESULT InternalClear()
	{
		HRESULT hr = Clear();
		ATLASSERT(SUCCEEDED(hr));
		if (FAILED(hr))
		{
			vt = VT_ERROR;
			scode = hr;
#ifndef _ATL_NO_VARIANT_THROW
			AtlThrow(hr);
#endif
		}
		return hr;
	}

	void InternalCopy(__in const PROPVARIANT* pSrc)
	{
		HRESULT hr = Copy(pSrc);
		if (FAILED(hr))
		{
			vt = VT_ERROR;
			scode = hr;
#ifndef _ATL_NO_VARIANT_THROW
			AtlThrow(hr);
#endif
		}
	}
};
